﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.RestrictedUsage.CSharp.Compiler;
using EnvDTE;
using Microsoft.RestrictedUsage.CSharp.Core;
using Microsoft.RestrictedUsage.CSharp.Syntax;
using Microsoft.RestrictedUsage.CSharp.Semantics;
using Microsoft.VisualStudio.CSharp.Services.Language.Interop;
using Microsoft.VisualStudio.Shell.Interop;
using devtm.Documentation.HierarchyModel;
using Microsoft.RestrictedUsage.CSharp.Extensions;
using System.Collections.ObjectModel;
using devtm.Collections;
using System.Text.RegularExpressions;
using CSharpParser.Structure;
using System.IO;
using CustomTypes.Interfaces.Structure;



namespace CSharpParser.CSharpModel
{

    public static class HierarchyParser
    {

        /// <summary>
        /// Parses the specified project.
        /// </summary>
        /// <param name="project">The project.</param>
        /// <param name="source">The source.</param>
        /// <returns></returns>
        public static void Parse(Project project, string source, IMemberStructure lst)
        {

            try
            {

                if ((project != null) && !HierarchyUtilities.IsMiscellaneousProject(project))
                {

                    Compilation compilation = CSharpTextSelection.LanguageModel.CreateCompiler(project).GetCompilation();

                    SourceFile file = compilation.SourceFiles[new FileName(source)];

                    foreach (ParseTreeNode node in file.GetParseTree().SelectFileCodeItems())
                    {
                        if (node is AttributeSectionNode)
                            continue;

                        CSharpType typeDeclaration = compilation.GetTypeFromTypeDeclaration(node);

                        if (typeDeclaration != null)
                            CreateType(typeDeclaration, lst as MemberCSharpStructure);

                    }
                }
            }
            catch
            {

            }

        }



        /// <summary>
        /// Parses the specified project.
        /// </summary>
        /// <param name="project">The project.</param>
        /// <param name="source">The source.</param>
        /// <returns></returns>
        public static void Parse(Project project, IMemberStructure lst)
        {

            try
            {

                if ((project != null) && !HierarchyUtilities.IsMiscellaneousProject(project))
                {

                    Compilation compilation = CSharpTextSelection.LanguageModel.CreateCompiler(project).GetCompilation();

                    IList<CSharpType> oo = compilation.MainAssembly.Types;

                    foreach (CSharpType node in oo)
                    {
                        CreateType(node, lst as MemberCSharpStructure);
                    }
                }
            }
            catch
            {

            }

        }




        internal static TypeCSharpStructure CreateType(CSharpType type, MemberCSharpStructure lst)
        {

            TypeCSharpStructure t = null;

            if (lst != null)
            {
                NamespaceCSharpStructure nameSpace = SetAndCreateNamespace(type, lst);

                string key = GetName(type);

                if (nameSpace.ContainsKey(key))
                    t = nameSpace[key] as TypeCSharpStructure;
                else
                    t = new TypeCSharpStructure(nameSpace, key) { CSharpString = type.ToString() };
            }


            if (t.initialized)
                return t;

            #region properties

            t.IsAnsiClass = true;
            t.IsSerializable = type.BaseInterfaces.Select(c => c.Name.Text).Where(d => d == "SerializableAttribute" || d == "DataContractAttribute").Any();
            t.IsAbstract = type.IsAbstract;
            t.IsArray = type.IsArray;
            t.IsAnonymousType = type.IsAnonymousType;
            t.IsClass = type.IsClass;
            t.IsConstructed = type.IsConstructed;
            t.IsContravariant = type.IsContravariant;
            t.IsCovariant = type.IsCovariant;
            t.IsDelegate = type.IsDelegate;
            t.IsDynamic = type.IsDynamic;
            t.IsEnum = type.IsEnum;
            t.IsInterface = type.IsInterface;
            t.IsInternal = type.IsInternal;
            t.IsNested = type.IsNested;
            t.IsPointer = type.IsPointer;
            t.IsPredefinedType = type.IsPredefinedType;
            t.IsPrivate = type.IsPrivate;
            t.IsProtected = type.IsProtected;
            t.IsProtectedOrInternal = type.IsProtectedOrInternal;
            t.IsPublic = type.IsPublic;
            t.IsReferenceType = type.IsReferenceType;
            t.IsSealed = type.IsSealed;
            t.IsStatic = type.IsStatic;
            t.IsStruct = type.IsStruct;
            t.IsTypeParameter = type.IsTypeParameter;
            t.IsUnknown = type.IsUnknown;
            t.IsValueType = type.IsValueType;



            #endregion

            CreateInterfaces(type.BaseInterfaces, t);

            CreateAttributes(type.Attributes, t);

            if (type.BaseClass != null)
            {
                IMemberStructure root = GetRoot(t);
                t.BaseType = FindItem(type.BaseClass, root);
            }


            CreateNestedTypes(type.NestedTypes, t);

            foreach (SourceLocation item in type.SourceLocations)
                t.Locations.Add
                    (new Location()
                    {
                        File = item.FileName.Value,
                        Ligne = item.Position.Line,
                        Colonne = item.Position.Character,
                        IsValid = item.Position.IsValid
                    }
                    );


            CreateNestedTypes(type.NestedTypes, t);
            CreateTypeArguments(type.TypeArguments, t);
            CreateTypeContrains(type.TypeConstraints, t);
            CreateTypeParameterPosition(type.TypeParameterPosition, t);
            CreateTypeParameters(type.TypeParameters, t);

            if (type.UnderlyingType != null)
            {
                IMemberStructure root = GetRoot(t);
                t.UnderlyingType = FindItem(type.UnderlyingType, root);
            }

            //CreateDocumentation(type.XMLDocumentation, t);

            //t.ImageName = InitializeImg(type);


            CreateMembers(type.Members, t);

            t.initialized = true;

            ParseDocumentation(type.XMLDocumentation, t);

            return t;

        }

        private static void ParseDocumentation(XMLDocInfo xMLDocInfo, IMemberStructure member)
        {

            if (xMLDocInfo != null)
            {

                string doc = xMLDocInfo.RawText;

                doc = doc.Substring(5);

                doc = doc.Substring(0, doc.Length - 6);

                member.Documentation = doc;

            }

        }

        private static string GetName(CSharpType type)
        {


            int index = type.ToString().IndexOf(type.Name.Text);

            string ret = type.ToString().Substring(index);





            //if (!type.IsArray)
            //{

            //if (type.Name != null)
            //{

            //    ret = type.Name.Text;

            //    if (type.TypeParameters.Count > 0)
            //    {
            //        ret += "<";
            //        bool t = false;
            //        foreach (var item in type.TypeParameters)
            //        {
            //            if (t)
            //                ret += ", ";
            //            ret += item.GetFullTypeName();
            //            t = true;
            //        }
            //        ret += ">";                     


            //    }

            //}
            //else
            //{

            //}

            //ret = type.ToString();

            //}
            //else
            //{

            //    if (type.ElementType.Name != null)
            //    {

            //        ret = GetName(type.ElementType);

            //        for (int i = 0; i < type.Rank; i++)
            //            ret += "[]";

            //    }
            //    else
            //    {

            //    }

            //}

            return ret;
        }

        #region Others



        private static void CreateNestedTypes(IList<CSharpType> iList, TypeCSharpStructure t)
        {

        }

        private static void CreateAttributes(IList<CSharpCustomAttribute> iList, MemberCSharpStructure t)
        {
            foreach (CSharpCustomAttribute item in iList)
                CreateAttributes(item, t);
        }

        private static void CreateAttributes(CSharpCustomAttribute item, MemberCSharpStructure t)
        {
            AttributeCSharpStructure attr = new AttributeCSharpStructure(t, t.GetHashCode().ToString());
        }

        private static void CreateInterfaces(IList<CSharpType> iList, TypeCSharpStructure t)
        {
            foreach (CSharpType item in iList)
                CreateType(item, t);
        }

        private static void CreateTypeParameters(IList<CSharpType> iList, MemberCSharpStructure t)
        {

        }

        private static void CreateTypeParameterPosition(int p, MemberCSharpStructure t)
        {

        }

        private static void CreateTypeContrains(IList<CSharpType> iList, MemberCSharpStructure t)
        {
        }

        private static void CreateTypeArguments(IList<CSharpType> iList, MemberCSharpStructure t)
        {

        }

        #endregion

        #region Members

        private static void CreateMembers(IList<CSharpMember> iList, TypeCSharpStructure t)
        {
            foreach (CSharpMember i in iList)
            {


                MemberCSharpStructure ret = null;

                try
                {

                    if (i.IsMethod)
                        ret = CreateMethod(i, t);

                    else if (i.IsEvent)
                        ret = CreateEvent(i, t);

                    else if (i.IsField)
                        ret = CreateField(i, t);

                    else if (i.IsOperator)
                        ret = CreateOperator(i, t);

                    else if (i.IsProperty)
                        ret = CreateProperty(i, t);

                    else if (i.IsEnumMember)
                        ret = CreateEnum(i, t);

                    //else if (i.IsFixedSizeBuffer)
                    //    ret = CreateFixedSizeBuffer(i, t);

                    if (ret == null)
                        continue;


                    if (i.IsPrivate)
                        ret.Accessibility = MemberVisibility.Private;
                    else if (i.IsPublic)
                        ret.Accessibility = MemberVisibility.Public;
                    else if (i.IsProtectedOrInternal)
                        ret.Accessibility = MemberVisibility.InternalProtected;
                    else if (i.IsProtected)
                        ret.Accessibility = MemberVisibility.Protected;
                    else if (i.IsInternal)
                        ret.Accessibility = MemberVisibility.Internal;


                    //foreach (SourceLocation item in i.SourceLocations)
                    //    t.Locations.Add
                    //        (
                    //          new Location()
                    //          {
                    //              File = item.FileName.Value,
                    //              Ligne = item.Position.Line,
                    //              Colonne = item.Position.Character,
                    //              IsValid = item.Position.IsValid
                    //          }
                    //        );

                    CreateTypeArguments(i.TypeArguments, t);
                    CreateTypeParameters(i.TypeParameters, t);

                    ParseDocumentation(i.XMLDocumentation, ret);

                }
                catch (Exception)
                {


                }


            }


            // ajouter les parametres, la porte et le static sur la clef pour qu'elle soit unique
            // associer les methodes et les proprietes.

        }

        private static FieldCSharpStructure CreateEnum(CSharpMember i, TypeCSharpStructure t)
        {

            string key = i.Name.Text;

            if (t.ContainsKey(key))
                return t[key] as FieldCSharpStructure;

            FieldCSharpStructure f = new FieldCSharpStructure(t, key) { CSharpString = i.ToString() };

            return f;
        }


        #region Methods


        private static MethodCSharpStructure CreateMethod(CSharpMember i, TypeCSharpStructure t)
        {

            string key = BuildKeyMethod(i, t);

            if (t.ContainsKey(key))
                return t[key] as MethodCSharpStructure;

            MethodCSharpStructure m = new MethodCSharpStructure(t, i.Name.Text, key) { CSharpString = i.ToString() };

            if (i.IsConstructor)
                m.MemberType = "Constructor";

            else if (i.IsDestructor)
                m.MemberType = "Destructor";

            if (i.Parameters != null)
                foreach (CSharpParameter item in i.Parameters.OrderBy(c => c.Position))
                    CreateParameter(item, m);

            m.IsExternal = i.IsExtern;

            m.IsAbstract = i.IsAbstract;
            m.IsIndexer = i.IsIndexer;
            m.IsNew = i.IsNew;
            m.IsOverride = i.IsOverride;

            m.IsFinal = i.IsSealed;
            m.IsStatic = i.IsStatic;
            m.IsVirtual = i.IsVirtual;

            if (i.ReturnType != null)
            {
                IMemberStructure root = GetRoot(t);
                m.Type = FindItem(i.ReturnType, root);
            }
            return m;
        }

        private static IMemberStructure FindItem(CSharpType cSharpType, IMemberStructure root)
        {

            bool test = cSharpType.IsTypeParameter;
            if (!test)
            {
                string assName = cSharpType.Assembly.BinaryFileName.Value;
                string assName2 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), @"Microsoft.NET\Framework");
                string assName3 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
                string assName4 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
                test = assName.StartsWith(assName2) || assName.StartsWith(assName3) || assName.StartsWith(assName4);
            }

            if (test)
            {

                string a = GetName(cSharpType);
                string n = String.Empty;

                if (!cSharpType.IsArray)
                {
                    if (cSharpType.ContainingNamespace != null)
                        n = cSharpType.ContainingNamespace.GetFullNamespace();
                    else
                    {

                    }
                }
                else
                {
                    if (cSharpType.ElementType.ContainingNamespace != null)
                        n = cSharpType.ElementType.ContainingNamespace.GetFullNamespace();
                    else
                    {

                    }
                }

                return new TypeCSharpStructure(new MemberCSharpStructure(null, n, null), a);

            }



            NamespaceCSharpStructure nameSpace = SetAndCreateNamespace(cSharpType, root as MemberCSharpStructure);

            string key1 = GetName(cSharpType);

            TypeCSharpStructure t = null;

            if (nameSpace.ContainsKey(key1))
                t = nameSpace[key1] as TypeCSharpStructure;
            else
                t = new TypeCSharpStructure(nameSpace, key1) { CSharpString = cSharpType.ToString() };

            return t;

        }


        private static IMemberStructure GetRoot(IMemberStructure t)
        {
            if (t.Parent == null)
                return t;
            else
            {
                return GetRoot(t.Parent);
            }
        }

        private static string BuildKeyMethod(CSharpMember i, TypeCSharpStructure t)
        {

            string ret = string.Empty;

            if (i.ReturnType != null)
            {

                string n = string.Empty;

                if (i.ToString().ToLower() != "void")
                {

                    if (i.ReturnType.ContainingNamespace != null)
                        ret += i.ReturnType.ContainingNamespace.GetFullNamespace() + ".";

                    ret += i.ReturnType.Name.Text + " ";
                }

                n += GetName(i.ReturnType);
            }

            return ret + i.ToString();
        }

        private static ParameterCSharpStructure CreateParameter(CSharpParameter i, MethodCSharpStructure m)
        {

            string key = i.Name.Text;

            if (m.ContainsKey(key))
                return m[key] as ParameterCSharpStructure;

            ParameterCSharpStructure p = new ParameterCSharpStructure(m, key) { CSharpString = i.ToString() };

            p.IsIn = !i.IsOut && !i.IsRef;
            p.IsRef = i.IsRef;
            p.IsOut = i.IsOut;
            p.IsThis = i.IsThis;
            p.IsParams = i.IsParams;

            if (i.DefaultValue != null)
                CreateDefaultValue(i.DefaultValue, p);


            if (i.Type != null)
            {
                IMemberStructure root = GetRoot(m);
                p.Type = FindItem(i.Type, root);
            }
            else
            {

            }

            return p;

        }

        private static void CreateDefaultValue(CSharpDefaultParameterValue cSharpDefaultParameterValue, ParameterCSharpStructure p)
        {

            p.IsOptional = true;

        }

        #endregion


        private static FieldCSharpStructure CreateField(CSharpMember i, TypeCSharpStructure t)
        {

            string key = i.Name.Text;

            if (t.ContainsKey(key))
                return t[key] as FieldCSharpStructure;

            FieldCSharpStructure f = new FieldCSharpStructure(t, key) { CSharpString = i.ToString() };

            f.IsIndexer = i.IsIndexer;
            f.IsStatic = i.IsStatic;
            f.IsReadonly = i.IsReadonly;
            f.IsConstant = i.IsConstant;
            f.IsVolatile = i.IsVolatile;

            if (i.ReturnType != null)
            {
                IMemberStructure root = GetRoot(t);
                f.Type = FindItem(i.ReturnType, root);
            }

            return f;
        }

        private static PropertyCSharpStructure CreateProperty(CSharpMember i, TypeCSharpStructure t)
        {

            string key = i.Name.Text;

            if (t.ContainsKey(key))
                return t[key] as PropertyCSharpStructure;

            PropertyCSharpStructure p = new PropertyCSharpStructure(t, key) { CSharpString = i.ToString() };

            p.IsAbstract = i.IsAbstract;
            p.IsIndexer = i.IsIndexer;
            p.IsNew = i.IsNew;
            p.IsOverride = i.IsOverride;

            p.IsFinal = i.IsSealed;
            p.IsStatic = i.IsStatic;
            p.IsVirtual = i.IsVirtual;

            if (i.ReturnType != null)
            {
                IMemberStructure root = GetRoot(t);
                p.Type = FindItem(i.ReturnType, root);
            }

            return p;
        }

        private static EventCSharpStructure CreateEvent(CSharpMember i, TypeCSharpStructure t)
        {

            string key = i.Name.Text;

            if (t.ContainsKey(key))
                return t[key] as EventCSharpStructure;

            EventCSharpStructure e = new EventCSharpStructure(t, key) { CSharpString = i.ToString() };

            // IsAddAccessor 
            // IsRemoveAccessor 



            return e;
        }

        private static OperatorCSharpStructure CreateOperator(CSharpMember i, TypeCSharpStructure t)
        {

            string key = i.Name.Text;

            if (t.ContainsKey(key))
                return t[key] as OperatorCSharpStructure;

            OperatorCSharpStructure o = new OperatorCSharpStructure(t, key) { CSharpString = i.ToString() };

            if (i.ReturnType != null)
            {
                IMemberStructure root = GetRoot(t);
                o.Type = FindItem(i.ReturnType, root);
            }

            return o;
        }

        private static void CreateFixedSizeBuffer(CSharpMember i, TypeCSharpStructure t)
        {
            throw new NotImplementedException();
        }


        #endregion

        #region Add namespace

        private static NamespaceCSharpStructure SetAndCreateNamespace(CSharpType type, MemberCSharpStructure lst)
        {


            MemberCSharpStructure ret = SetAndCreateNamespace(type.ContainingNamespace, lst);

            string n = type.ContainingNamespace.Name.Text;

            if (!ret.ContainsKey(n))
            {
                NamespaceCSharpStructure n1 = new NamespaceCSharpStructure(ret, n) { CSharpString = type.ToString() };
                return (NamespaceCSharpStructure)n1;
            }
            return (NamespaceCSharpStructure)ret[n];
        }

        private static MemberCSharpStructure SetAndCreateNamespace(CSharpNamespace type, MemberCSharpStructure lst)
        {

            MemberCSharpStructure ret = null;

            if (type.ContainingNamespace != null)
                ret = SetAndCreateNamespace(type.ContainingNamespace, lst);
            else
                return null;

            string n = type.ContainingNamespace.Name.Text;

            if (string.IsNullOrEmpty(n))
                return lst;

            MemberCSharpStructure list = ret ?? lst;

            if (!list.ContainsKey(n))
                return new NamespaceCSharpStructure(ret, n) { CSharpString = type.ToString() };
            else
                return list[n] as MemberCSharpStructure;

        }

        #endregion


    }

}
